#include "game_sim_region.h"
#include "game.h"
#include "game_debug.h"
#include "game_world_mode.h"
#include "game_entity.h"
#include "game_config.h"

entity_traversable_point *GetTraversable(entity *Entity, uint32 Index) {
    entity_traversable_point *Result = 0;
    if (Entity) {
        Assert(Index < Entity->TraversableCount);
        Result = Entity->Traversables + Index;
    }
    return Result;
}

entity_traversable_point *GetTraversable(traversable_reference Reference) {
    entity_traversable_point *Result = GetTraversable(Reference.Entity.Ptr, Reference.Index);
    return Result;
}

entity_traversable_point GetSimSpaceTraversable(entity *Entity, uint32 Index) {
    entity_traversable_point Result = {};
    if (Entity) {
        Result.P = Entity->P;
        entity_traversable_point *Point = GetTraversable(Entity, Index);
        if (Point) {
            Result.P += Point->P;
            Result.Occupier = Point->Occupier;
        }
    }
    return Result;
}

entity_traversable_point GetSimSpaceTraversable(traversable_reference Reference) {
    entity_traversable_point Result = GetSimSpaceTraversable(Reference.Entity.Ptr, Reference.Index);
    return Result;
}

// 从给定的模拟区域中通过存储索引获取哈希表中的实体
internal entity_hash *GetHashFromID(sim_region *SimRegion, entity_id StorageIndex) {
    entity_hash *Result = 0;  // 默认返回值为null，表示没有找到匹配项

    // 确保存储索引不为0
    if (StorageIndex.Value) {
        uint32 HashValue = StorageIndex.Value;  // 使用存储索引作为哈希值

        // 遍历哈希表，处理哈希冲突
        for (uint32 Offset = 0; Offset < ArrayCount(SimRegion->EntityHash); ++Offset) {
            // 计算哈希掩码，HashMask 用于限制哈希表索引的范围，
            // 通过减 1 确保掩码是哈希表大小减 1 的值，这样可以适配基于 2 的幂的表大小。
            uint32 HashMask = (ArrayCount(SimRegion->EntityHash) - 1);

            // 计算当前偏移位置的哈希索引，HashIndex 是通过将 HashValue 加上 Offset 后，
            // 按位与 HashMask 得到的，确保计算结果在有效的哈希表范围内。
            uint32 HashIndex = ((HashValue + Offset) & HashMask);

            // 根据哈希值和偏移量计算当前位置的哈希表条目
            entity_hash *Entry = SimRegion->EntityHash + HashIndex;

            // 如果条目为空（索引为0）或索引匹配，则找到对应的实体
            if ((Entry->Index.Value == 0) || (Entry->Index.Value == StorageIndex.Value)) {
                Result = Entry;  // 将找到的条目赋值给结果
                break;           // 找到后跳出循环
            }
        }
    }
    return Result;  // 返回找到的实体哈希条目（如果有）
}

brain_hash *GetHashFromID(sim_region *SimRegion, brain_id BrainID) {
    brain_hash *Result = 0;  // 默认返回值为null，表示没有找到匹配项

    // 确保存储索引不为0
    if (BrainID.Value) {
        uint32 HashValue = BrainID.Value;  // 使用存储索引作为哈希值

        // 遍历哈希表，处理哈希冲突
        for (uint32 Offset = 0; Offset < ArrayCount(SimRegion->EntityHash); ++Offset) {
            // 计算哈希掩码，HashMask 用于限制哈希表索引的范围，
            // 通过减 1 确保掩码是哈希表大小减 1 的值，这样可以适配基于 2 的幂的表大小。
            uint32 HashMask = (ArrayCount(SimRegion->BrainHash) - 1);

            // 计算当前偏移位置的哈希索引，HashIndex 是通过将 HashValue 加上 Offset 后，
            // 按位与 HashMask 得到的，确保计算结果在有效的哈希表范围内。
            uint32 HashIndex = ((HashValue + Offset) & HashMask);

            // 根据哈希值和偏移量计算当前位置的哈希表条目
            brain_hash *Entry = SimRegion->BrainHash + HashIndex;

            // 如果条目为空（索引为0）或索引匹配，则找到对应的实体
            if ((Entry->ID.Value == 0) || (Entry->ID.Value == BrainID.Value)) {
                Result = Entry;  // 将找到的条目赋值给结果
                break;           // 找到后跳出循环
            }
        }
    }
    return Result;  // 返回找到的实体哈希条目（如果有）
}

// 将存储索引映射到对应的实体
internal void MapStorageIndexToEntity(sim_region *SimRegion, entity_id StorageIndex,
                                      entity *Entity) {
    // 获取哈希表中的条目
    entity_hash *Entry = GetHashFromID(SimRegion, StorageIndex);

    // 确保哈希表条目为空或已经匹配存储索引
    Assert((Entry->Index.Value == 0) || (Entry->Index.Value == StorageIndex.Value));

    // 将存储索引和实体指针保存到哈希表中
    Entry->Index = StorageIndex;
    Entry->Ptr = Entity;
}

// 将实体引用存储为索引
void PackEntityReference(sim_region *SimRegion, entity_reference *Ref) {
    if (Ref->Ptr) {
        if (IsDeleted(Ref->Ptr)) {
            Ref->Index.Value = 0;
        } else {
            Ref->Index = Ref->Ptr->ID;
        }
    } else {
        if (SimRegion && GetHashFromID(SimRegion, Ref->Index)) {
            Ref->Index.Value = 0;
        }
    }
}
void PackTraversableReference(sim_region *SimRegion, traversable_reference *Ref) {  //
    (void)Ref;
    PackEntityReference(SimRegion, &Ref->Entity);
}
inline entity *GetEntityByID(sim_region *SimRegion, entity_id ID) {
    entity_hash *Entry = GetHashFromID(SimRegion, ID);
    entity *Result = Entry ? Entry->Ptr : 0;
    return Result;
}
// 加载实体引用，恢复对应的实体指针
inline void LoadEntityReference(sim_region *SimRegion, entity_reference *Ref) {
    if (Ref->Index.Value) {
        Ref->Ptr = GetEntityByID(SimRegion, Ref->Index);
    }
}

void LoadTraversableReference(sim_region *SimRegion, traversable_reference *Ref) {
    LoadEntityReference(SimRegion, &Ref->Entity);
}

// 通过存储索引获取实体
inline entity *GetEntityByStorageIndex(sim_region *SimRegion, entity_id StorageIndex) {
    // 从哈希表中获取实体条目
    entity_hash *Entry = GetHashFromID(SimRegion, StorageIndex);

    // 返回实体指针
    entity *Result = Entry->Ptr;
    return (Result);
}

// 函数：检查实体是否与给定的矩形相交
internal bool32 EntityOverlapsRectangle(v3 P, entity_collision_volume Volume, rectangle3 Rect) {
    // 将矩形的大小增加实体维度的一半（考虑实体的大小）
    rectangle3 Grown = AddRadiusTo(Rect, 0.5f * Volume.Dim);

    // 检查实体位置 P 是否在扩展后的矩形内
    bool32 Result = IsInRectangle(Grown, P + Volume.OffsetP);

    // 返回检查结果
    return Result;
}

internal void ConnectEntityPointers(sim_region *SimRegion) {
    TIMED_FUNCTION();
    for (uint32 EntityIndex = 0; EntityIndex < SimRegion->EntityCount; ++EntityIndex) {
        entity *Entity = SimRegion->Entities + EntityIndex;
        LoadTraversableReference(SimRegion, &Entity->Occupying);
        if (Entity->Occupying.Entity.Ptr) {
            Entity->Occupying.Entity.Ptr->Traversables[Entity->Occupying.Index].Occupier = Entity;
        }
        LoadTraversableReference(SimRegion, &Entity->CameFrom);
    }
}

// 初始化模拟过程
// 参数:
// Center: 模拟区域的中心位置
// Bounds: 模拟区域的边界矩形
sim_region *BeginSim(memory_arena *SimArena, world *World, world_position Origin, rectangle3 Bounds,
                     real32 dt) {
    TIMED_FUNCTION();
    // 分配并初始化一个 sim_region 结构体，用于模拟区域的核心数据
    sim_region *SimRegion = PushStruct(SimArena, sim_region);

    // 将当前世界指针赋值给 SimRegion，用于记录此模拟区域所属的世界
    SimRegion->World = World;

    SimRegion->MaxEntityRadius = 5.0f;
    SimRegion->MaxEntityVelocity = 30.0f;
    // TODO: 最终从所有实体的半径的最大值加上它们的速度来计算这个值
    real32 UpdateSafeMargin =
        SimRegion->MaxEntityRadius + dt * SimRegion->MaxEntityVelocity;  // 更新安全边界 = 1.0f

    real32 UpdateSafeMarginZ = 1.0f;

    // 初始化模拟区域的原点，用于确定区域的世界空间位置
    SimRegion->Origin = Origin;

    // 将传入的矩形区域 Bounds 赋值给 SimRegion 的 UpdateBounds 属性，
    // 以更新模拟区域的边界。
    // 将模拟区域的边界扩展，增加最大实体半径，确保模拟区域足够大以包含实体
    SimRegion->UpdateBounds = AddRadiusTo(Bounds,
                                          v3{SimRegion->MaxEntityRadius,  // X轴扩展最大实体半径
                                             SimRegion->MaxEntityRadius,  // Y轴扩展最大实体半径
                                             SimRegion->MaxEntityRadius});  // Z轴扩展最大实体半径

    // 将更新安全边际（UpdateSafeMargin）应用到模拟区域（SimRegion）的更新边界（UpdateBounds），
    // 扩展矩形的范围以确保包括额外的安全裕度。
    SimRegion->Bounds = AddRadiusTo(SimRegion->UpdateBounds,
                                    v3{UpdateSafeMargin, UpdateSafeMargin, UpdateSafeMarginZ});

    // 设定模拟区域内允许的最大实体数量，确保有足够空间容纳实体
    SimRegion->MaxEntityCount = 4096;

    // 初始化模拟区域当前的实体计数为 0，后续会根据实际情况动态增加
    SimRegion->EntityCount = 0;

    // 分配一个实体数组，用于存储模拟区域中的所有实体
    SimRegion->Entities = PushArray(SimArena, SimRegion->MaxEntityCount, entity, NoClear());

    SimRegion->MaxBrainCount = 256;
    SimRegion->BrainCount = 0;
    SimRegion->Brains = PushArray(SimArena, SimRegion->MaxBrainCount, brain, NoClear());

    // 计算并存储模拟区域最小角落在世界空间中的位置
    world_position MinChunkP =
        MapIntoChunkSpace(World, SimRegion->Origin, GetMinCorner(SimRegion->Bounds));
    // 计算并存储模拟区域最大角落在世界空间中的位置
    world_position MaxChunkP =
        MapIntoChunkSpace(World, SimRegion->Origin, GetMaxCorner(SimRegion->Bounds));

    // 遍历SimRegion范围内的所有块
    for (int32 ChunkZ = MinChunkP.ChunkZ; ChunkZ <= MaxChunkP.ChunkZ; ChunkZ++) {
        for (int32 ChunkY = MinChunkP.ChunkY; ChunkY <= MaxChunkP.ChunkY; ++ChunkY) {
            for (int32 ChunkX = MinChunkP.ChunkX; ChunkX <= MaxChunkP.ChunkX; ++ChunkX) {
                world_chunk *Chunk = RemoveWorldChunk(World, ChunkX, ChunkY, ChunkZ);
                if (Chunk) {
                    world_position ChunkPosition = {ChunkX, ChunkY, ChunkZ};

                    // inline v3 GetSimSpaceP(sim_region *SimRegion, entity *Stored) {
                    //     v3 Result = Subtract(SimRegion->World, &Stored->ChunkP,
                    //     &SimRegion->Origin);
                    //     return (Result);
                    // }

                    v3 ChunkDelta = Subtract(SimRegion->World, &ChunkPosition, &SimRegion->Origin);
                    world_entity_block *Block = Chunk->FirstBlock;

                    while (Block) {
                        // 遍历当前块中的所有低频实体
                        for (uint32 EntityIndex = 0; EntityIndex < Block->EntityCount;
                             EntityIndex++) {
                            entity *Low = (entity *)Block->EntityData + EntityIndex;
                            v3 SimSpaceP = Low->P + ChunkDelta;
                            // 检查实体是否在相机范围内
                            if (EntityOverlapsRectangle(SimSpaceP, Low->Collision->TotalVolume,
                                                        SimRegion->Bounds)) {
                                TIMED_BLOCK("SimUnpack");
                                entity_id ID = Low->ID;
                                entity_hash *Entry = GetHashFromID(SimRegion, ID);

                                // 根据存储索引从模拟区域的哈希表中获取对应的哈希条目
                                Assert(Entry->Ptr == 0);
                                if (SimRegion->EntityCount < SimRegion->MaxEntityCount) {
                                    entity *Dest = SimRegion->Entities + SimRegion->EntityCount++;
                                    Entry->Index = ID;
                                    Entry->Ptr = Dest;
                                    if (Low) {
                                        // TODO: 这实际上应该是一个解压缩步骤，而不是复制。
                                        *Dest = *Low;
                                        Dest->ZLayer = ChunkZ;
                                        Dest->ManualSort = {};
                                    }
                                    Dest->ID = ID;
                                    Dest->P += ChunkDelta;

                                    // 判断目标实体是否与模拟区域的更新边界重叠，如果重叠则设置实体的可更新状态
                                    if (EntityOverlapsRectangle(Dest->P,
                                                                Dest->Collision->TotalVolume,
                                                                SimRegion->UpdateBounds)) {
                                        Dest->Flags |= EntityFlag_Active;
                                    }
                                    if (Dest->BrainID.Value) {
                                        brain *Brain =
                                            GetOrAddBrain(SimRegion, Dest->BrainID,
                                                          (brain_type)Dest->BrainSlot.Type);
                                        uint8 *Ptr = (uint8 *)&Brain->Array;
                                        Ptr += sizeof(entity *) * Dest->BrainSlot.Index;
                                        Assert(Ptr <=
                                               ((uint8 *)Brain + sizeof(brain) - sizeof(entity *)));
                                        *((entity **)Ptr) = Dest;
                                    }
                                } else {
                                    // 如果没有空间，标记为无效的代码路径
                                    InvalidCodePath;
                                }
                            }
                        }
                        world_entity_block *NextBlock = Block->Next;
                        AddBlockToFreeList(World, Block);
                        Block = NextBlock;
                    }
                    AddChunkToFreeList(World, Chunk);
                }
            }
        }
    }
    ConnectEntityPointers(SimRegion);
    return (SimRegion);
}

// 结束模拟过程并清理资源
// 参数:
// Region: 指向模拟区域的指针，表示当前模拟的区域
void EndSim(sim_region *Region, game_mode_world *WorldMode) {
    TIMED_FUNCTION();
    //  获取模拟区域中的实体数组
    entity *Entity = Region->Entities;
    world *World = WorldMode->World;
    // 遍历模拟区域内的所有实体
    for (uint32 EntityIndex = 0; EntityIndex < Region->EntityCount; ++EntityIndex, Entity++) {
        if (!(Entity->Flags & EntityFlag_Deleted)) {
            world_position EntityP = MapIntoChunkSpace(World, Region->Origin,
                                                       Entity->P);  // 将实体位置映射到块空间
            world_position ChunkP = EntityP;
            ChunkP.Offset_ = v3{0, 0, 0};

            v3 ChunkDelta = -Subtract(Region->World, &ChunkP, &Region->Origin);
            if (Entity->ID.Value == WorldMode->CameraFollowingEntityIndex.Value) {
                // 记录摄像机当前位置，用于计算相对于摄像机的偏移量
                world_position NewCameraP = WorldMode->CameraP;

                // 更新摄像机的Z轴位置，使其与跟随实体的Z轴位置一致
                v3 RoomDelta = {24.0f, 12.5f, WorldMode->TypicalFloorHeight};
                v3 hRoomDelta = 0.5f * RoomDelta;
                real32 ApronSize = 0.7f;
                real32 BoundHeight = 0.5f;
                v3 hRoomApron = {hRoomDelta.x - ApronSize, hRoomDelta.y - ApronSize,
                                 hRoomDelta.z - ApronSize};
                if (Global_Renderer_Camera_UseRoomBase) {
                    WorldMode->CameraOffset = v3{0, 0, 0};
                    v3 AppliedDelta = {};
                    for (uint32 E = 0; E < 3; ++E) {
                        if (Entity->P.E[E] > hRoomDelta.E[E]) {
                            AppliedDelta.E[E] = RoomDelta.E[E];
                            NewCameraP = MapIntoChunkSpace(World, NewCameraP, AppliedDelta);
                        }

                        if (Entity->P.E[E] < -hRoomDelta.E[E]) {
                            AppliedDelta.E[E] = -RoomDelta.E[E];
                            NewCameraP = MapIntoChunkSpace(World, NewCameraP, AppliedDelta);
                        }
                    }
                    v3 vEntityP = Entity->P;
                    if (vEntityP.y > hRoomApron.y) {
                        real32 t = ClampMapToRange(hRoomApron.y, vEntityP.y, hRoomDelta.y);
                        WorldMode->CameraOffset =
                            v3{0.0f, t * hRoomDelta.y, (-(t * t) + 2.0f * t) * BoundHeight};
                    }

                    if (vEntityP.y < -hRoomApron.y) {
                        real32 t = ClampMapToRange(-hRoomApron.y, vEntityP.y, -hRoomDelta.y);
                        WorldMode->CameraOffset =
                            v3{0.0f, -t * hRoomDelta.y, (-(t * t) + 2.0f * t) * BoundHeight};
                    }
                    if (vEntityP.x > hRoomApron.x) {
                        real32 t = ClampMapToRange(hRoomApron.x, vEntityP.x, hRoomDelta.x);
                        WorldMode->CameraOffset =
                            v3{t * hRoomDelta.x, 0.0f, (-(t * t) + 2.0f * t) * BoundHeight};
                    }

                    if (vEntityP.x < -hRoomApron.x) {
                        real32 t = ClampMapToRange(-hRoomApron.x, vEntityP.x, -hRoomDelta.x);
                        WorldMode->CameraOffset =
                            v3{-t * hRoomDelta.x, 0.0f, (-(t * t) + 2.0f * t) * BoundHeight};
                    }
                    if (vEntityP.z > hRoomApron.z) {
                        real32 t = ClampMapToRange(hRoomApron.z, vEntityP.z, hRoomDelta.z);
                        WorldMode->CameraOffset = v3{0.0f, 0.0f, t * hRoomDelta.z};
                    }

                    if (vEntityP.z < -hRoomApron.z) {
                        real32 t = ClampMapToRange(-hRoomApron.z, vEntityP.z, -hRoomDelta.z);
                        WorldMode->CameraOffset = v3{0.0f, 0.0f, -t * hRoomDelta.z};
                    }
                } else {
                    NewCameraP = EntityP;
                }
                WorldMode->CameraP = NewCameraP;
            }
            Entity->P += ChunkDelta;
            PackEntityIntoWorld(World, Region, Entity, EntityP);
        }
    }
}

bool32 GetClosestTraversableAlongRay(sim_region *SimRegion, v3 FromP, v3 Dir,
                                     traversable_reference Skip, traversable_reference *Result,
                                     uint32 Flags) {
    TIMED_FUNCTION();
    bool32 Found = false;
    for (uint32 ProbeIndex = 0; ProbeIndex < 5; ++ProbeIndex) {
        v3 SampleP = FromP + 0.5f * (real32)ProbeIndex * Dir;
        if (GetClosestTraversable(SimRegion, SampleP, Result, Flags)) {
            if (!IsEqual(Skip, *Result)) {
                Found = true;
                break;
            }
        }
    }
    return Found;
}

bool32 GetClosestTraversable(sim_region *SimRegion, v3 FromP, traversable_reference *Result,
                             uint32 Flags) {
    TIMED_FUNCTION();
    bool32 Found = false;
    real32 ClosestDistanceSq = Square(1000.0f);
    entity *TestEntity = SimRegion->Entities;
    for (uint32 TestEntityIndex = 0; TestEntityIndex < SimRegion->EntityCount;
         ++TestEntityIndex, ++TestEntity) {
        for (uint32 PIndex = 0; PIndex < TestEntity->TraversableCount; ++PIndex) {
            entity_traversable_point P = GetSimSpaceTraversable(TestEntity, PIndex);
            if (!(Flags & TraversableSearch_Unoccupied) || (P.Occupier == 0)) {
                v3 ToPoint = P.P - FromP;
                // ToPoint.z = ClampAboveZero(AbsoluteValue(ToPoint.z) - 1.0f);
                real32 TestDsq = LengthSq(ToPoint);
                if (ClosestDistanceSq > TestDsq) {
                    Result->Entity.Ptr = TestEntity;
                    Result->Entity.Index = TestEntity->ID;
                    Result->Index = PIndex;
                    ClosestDistanceSq = TestDsq;
                    Found = true;
                }
            }
        }
    }
    if (!Found) {
        Result->Entity.Ptr = 0;
        Result->Index = 0;
    }
    return Found;
}

bool32 IsOccupied(traversable_reference Ref) {
    bool32 Result = 0;
    entity_traversable_point *Dest = GetTraversable(Ref);
    if (Dest) {
        Result = (Dest->Occupier != 0);
    }
    return Result;
}

bool32 TransactionalOccupy(entity *Entity, traversable_reference *DestRef,
                           traversable_reference DesiredRef) {
    bool32 Result = false;
    entity_traversable_point *Desired = GetTraversable(DesiredRef);
    if (Desired && !Desired->Occupier) {
        entity_traversable_point *Dest = GetTraversable(*DestRef);
        if (Dest) {
            Dest->Occupier = 0;
        }
        *DestRef = DesiredRef;
        Desired->Occupier = Entity;
        Result = true;
    }
    return Result;
}

closest_entity GetClosestEntityWithBrain(sim_region *SimRegion, v3 P, brain_type Type,
                                         real32 MaxRadio) {
    closest_entity Result = {};
    // 设置最大搜索范围为十米的平方
    Result.DistanceSq = Square(MaxRadio);  // 注意：最大搜索范围为十米
    // 遍历所有高频实体（可能是怪物、英雄等）
    entity *TestEntity = SimRegion->Entities;
    for (uint32 TestEntityIndex = 0; TestEntityIndex < SimRegion->EntityCount;
         TestEntityIndex++, TestEntity++) {
        // 如果该实体是一个英雄
        if (IsType(TestEntity->BrainSlot, Type)) {
            // 计算该英雄与目标实体之间的距离平方
            v3 TestDelta = TestEntity->P - P;
            real32 TestDSq = LengthSq(TestDelta);

            // 如果当前英雄比之前的英雄更接近目标实体，更新最近英雄的信息
            if (Result.DistanceSq > TestDSq) {
                Result.Entity = TestEntity;  // 更新最近的英雄实体
                Result.DistanceSq = TestDSq;
                Result.Delta = TestDelta;
            }
        }
    }
    return Result;
}